package com.jxy.bean;

import com.jxy.bean.attribute.Attribute;
import com.jxy.bean.constant.ClassInfo;
import com.jxy.bean.constant.ConstantPool;
import com.jxy.bean.constant.ConstantPoolTag;
import com.jxy.tool.Tool;

import javax.xml.bind.DatatypeConverter;
import java.io.*;

/*
ClassFile {
 u4 magic;
 u2 minor_version;
 u2 major_version;
 u2 constant_pool_count;
 cp_info constant_pool[constant_pool_count-1];
 u2 access_flags;
 u2 this_class;
 u2 super_class;
 u2 interfaces_count;
 u2 interfaces[interfaces_count];
 u2 fields_count;
 field_info fields[fields_count];
 u2 methods_count;
 method_info methods[methods_count];
 u2 attributes_count;
 attribute_info attributes[attributes_count];
}
 */
public class ClassFile {
    private byte[] magic;
    private String version;
    private ConstantPool[] pools;
    private int access_flags;
    private int this_class;
    private int super_class;
    private int[] interfaces;
    private FieldInfo[] fields;
    private MethodInfo[] methods;
    private Attribute[] attributes;

    public ClassFile(String fileName) {
        // 读取文件
        FileInputStream fis;
        try {
            fis = new FileInputStream(fileName);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return;
        }
        //数据流
        DataInputStream dis = new DataInputStream(fis);

        init(dis);

        try {
            dis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public ClassFile(byte[] data) {
        DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data));
        init(dis);
        try {
            dis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public ClassFile(DataInputStream dis) {
        init(dis);
    }

    public void init(DataInputStream dis) {
        magic = Tool.readMagic(dis);
        version = Tool.readVersion(dis);
        pools = Tool.readConstantPool(dis, this);
        access_flags = Tool.readU2(dis);
        this_class = Tool.readU2(dis);
        super_class = Tool.readU2(dis);
        interfaces = Tool.readInterface(dis);
        fields = Tool.readField(dis, this);
        methods = Tool.readMethod(dis, this);
        attributes = Tool.readAttribute(dis, this);
    }

    public MethodInfo[] getMethods() {
        return methods;
    }

    public String getMagic() {
        return DatatypeConverter.printHexBinary(magic);
    }

    public String getVersion() {
        return version;
    }

    public ConstantPool[] getPools() {
        return pools;
    }

    public String getAccessFlags() {
        return Tool.analyAccessFlag(Tool.class_access_flags, access_flags);
    }

    public String getThisClass() {
        return ((ClassInfo) pools[this_class]).getClassName();
    }

    public String getSuperClass() {
        ClassInfo classInfo = (ClassInfo) pools[super_class];
        return classInfo == null ? null : classInfo.getClassName();
    }

    public int[] getInterfaces() {
        return interfaces;
    }

    public FieldInfo[] getFields() {
        return fields;
    }

    public Attribute[] getAttributes() {
        return attributes;
    }

    public boolean valid() {
        //验证常量池
        for (int i = 1; i < pools.length; i++) {
            if (!pools[i].valid()) {
                return false;
            }
        }

        //this_class指向常量池的class类型
        if (pools[this_class].getTag() != ConstantPoolTag.CONSTANT_Class.getTag()) {
            return false;
        }
        //super_class指向常量池的class类型,或者0则父类为Object
        if (super_class != 0 && pools[super_class].getTag() != ConstantPoolTag.CONSTANT_Class.getTag()) {
            return false;
        }

        //interface指向常量池的class类型
        for (int inter : interfaces) {
            if (pools[inter].getTag() != ConstantPoolTag.CONSTANT_Class.getTag()) {
                return false;
            }
        }

        return true;
    }

    public void printClass() {
        System.out.println("Magic:" + getMagic());

        System.out.println("Version:" + getVersion());

        System.out.println("ConstantPool:");
        for (int i = 1; i < pools.length; i++) {
            System.out.println(i + "#" + pools[i]);
        }

        System.out.println("Access_flags:" + access_flags + "(" + getAccessFlags() + ")");

        System.out.println("This_class:" + this_class + "(" + pools[this_class] + ")");

        System.out.println("Super_class:" + super_class + "(" + pools[super_class] + ")");

        System.out.println("Interfaces:");
        for (int i = 0; i < interfaces.length; i++) {
            System.out.println("interfaces[" + i + "]:" + pools[interfaces[i]]);
        }

        System.out.println("Fields:");
        for (int i = 0; i < fields.length; i++) {
            System.out.println(fields[i]);
        }

        System.out.println("Methods:");
        for (int i = 0; i < methods.length; i++) {
            System.out.println(methods[i]);
        }

        System.out.println("Attributes:");
        for (int i = 0; i < attributes.length; i++) {
            System.out.println(attributes[i]);
        }
    }
}
